/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
// -*- coding:iso-8859-15 mode: c++ -*-
/** 
 * \file
 * \brief Player upper class implementation.
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 *
 * $Id: Player.cpp,v 1.14 2009-06-24 13:10:49 morsko Exp $
 *
 */
#include "Player.hpp"


Player::Player()
  :  CSync(1, 1),               
     CThread(1),       
     iPlaying(false),
     iRepeat(false),
     iPlayingSpeed(1),
     iActionTimes(),
     iCurrentTimeIterator()
{

}
//*****************************************************************************
Player::~Player(){}
//*****************************************************************************

void Player::StartPlaying()
{
  dPrint(10,"Start playing called");
  Lock();
  iPlaying = true;
  Unlock();
  RunThread(0);
}
//*****************************************************************************

void Player::PausePlaying()
{
  dPrint(10,"pause playing called");
  Lock();
  iPlaying = false;
  Unlock();
  SetRequestTermination();
  WaitThread(0);
}
//*****************************************************************************

void Player::StopPlaying()
{
  dPrint(10,"Stop playing called");
  Lock();
  iPlaying = false;
  Unlock();
  SetRequestTermination();
  WaitThread(0);
  Lock();
  iCurrentTimeIterator = iActionTimes.begin();
  Unlock();
}
//*****************************************************************************

void Player::SetPlayingSpeed(float aPlayingSpeed)
{
  iPlayingSpeed = aPlayingSpeed;

}
//*****************************************************************************

void Player::SetCurrentTime(gim::time aCurrentNewTime)
{

  bool play = iPlaying;
  if(iPlaying){
    Lock();
    iPlaying = false;
    Unlock();
    SetRequestTermination();
    WaitThread(0);

  }
  Lock();

  if(aCurrentNewTime > iCurrentTimeIterator->first){
    while(iCurrentTimeIterator != iActionTimes.end() && iCurrentTimeIterator->first < aCurrentNewTime){
      iCurrentTimeIterator++;

    }
    iCurrentTimeIterator--;
    
  }else{
    while(iCurrentTimeIterator != iActionTimes.begin() && iCurrentTimeIterator->first > aCurrentNewTime){
      iCurrentTimeIterator--;

    
    }
  }

  Unlock();
  if(play){
    
    iPlaying = true;
    RunThread(0);
  }
 
}
//*****************************************************************************

void Player::SetPlayerModule(PlayerModule *aModule)
{
  bool setStartingPoint = false;

  if(iActionTimes.empty()){
    setStartingPoint = true;
  }

  Lock();

  std::list<gim::time> tlist;

  aModule->GetTimes(&tlist);
  for(EACH_IN_i(tlist)){
   
    iActionTimes.insert(std::pair<gim::time,PlayerModule *>(*i,aModule));
    
  }

  if(setStartingPoint || !iPlaying){
    iCurrentTimeIterator = iActionTimes.begin();

  }
  Unlock();

}
//*****************************************************************************

void Player::RemovePlayerModule(PlayerModule *aModule)
{
  std::multimap<gim::time, PlayerModule*>::iterator i = iActionTimes.begin();

  while(i != iActionTimes.end()){

    if(i->second == aModule){
      i++;
      std::multimap<gim::time, PlayerModule*>::iterator i2 = i;
     
      --i2;
      iActionTimes.erase(i2);
          
    }

   
  }
  iCurrentTimeIterator = iActionTimes.begin();


}
//*****************************************************************************

int Player::ThreadFunction(const int aThreadNumber)
{
  dPrint(10,"Start player thread");
  std::multimap<gim::time, PlayerModule*>::iterator nextIterator = iCurrentTimeIterator;
  while(iPlaying){

    if(iCurrentTimeIterator != iActionTimes.end()){
      //call playermodule onTime function 

      iCurrentTimeIterator->second->OnTime(iCurrentTimeIterator->first);

      //Then calculate sleepingtime for the next action... 
      nextIterator++;
      if(nextIterator != iActionTimes.end()){
        gim::time sltime =  nextIterator->first - iCurrentTimeIterator->first; 
        
        int sleepingTime = (sltime.getSeconds()*1000 +sltime.getUSeconds()/1000)/iPlayingSpeed;

        ownSleep_ms(sleepingTime);
        iCurrentTimeIterator++;
      }else{

        if(iRepeat){

          Lock();
          iCurrentTimeIterator = iActionTimes.begin();
          nextIterator = iActionTimes.begin();
          Unlock();
        }else{

          //At the end...
          StopPlaying();
        }
      }
    }else{
      //At the end...
      if(iRepeat){

        Lock();
        iCurrentTimeIterator = iActionTimes.begin();
        nextIterator = iActionTimes.begin();
        Unlock();

      }else{             

        StopPlaying();
      }
    }
  }

  return 1;

}
//*****************************************************************************
bool Player::GetTimeLimits(gim::time &aBegin,
                           gim::time &aEnd)
{
  if(iActionTimes.size() >0){
    Lock();
    aBegin = iActionTimes.begin()->first;
    aEnd = iActionTimes.rbegin()->first;
    dPrint(15,"begin %s, end %s",aBegin.toString().c_str(),aEnd.toString().c_str());
    Unlock();
    return true;
  }else{
    return false;
  }


}
//*****************************************************************************
void Player::GetCurrentTime(gim::time &aCurrent, float &aRatioFromAll)
{
  if(iActionTimes.size() >0){
    Lock();
    gim::time begin = iActionTimes.begin()->first;
    gim::time end = iActionTimes.rbegin()->first;
    gim::time totalTime = end-begin;
    if(iCurrentTimeIterator != iActionTimes.end()){
      aCurrent = iCurrentTimeIterator->first -iActionTimes.begin()->first;
    }else{
      aCurrent = totalTime;
    }

   
    dPrint(14,"current %s, total %s",aCurrent.toString().c_str(),totalTime.toString().c_str());
    aRatioFromAll = (float)(aCurrent.getSeconds())/(float)(totalTime.getSeconds());
    dPrint(14,"ratio %f",aRatioFromAll);
    Unlock();
  }else{
    gim::time zero(0,0);
    aCurrent =zero;
    aRatioFromAll = 0;
  }
}
//*****************************************************************************
void Player::SetRepeat(bool state)
{
  Lock();
  iRepeat = state;
  Unlock();


}
//*****************************************************************************

bool Player::IsPlaying(void)
{
  return iPlaying;

}
//*****************************************************************************

bool Player::IsRepeat(void)
{
  return iRepeat;
}
//*****************************************************************************
